﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Markup;

namespace MetroToolbox.Interactivity
{
    public abstract class TriggerBase<T> : TriggerBase where T : DependencyObject
    {
        protected override Type AssociatedObjectTypeConstraint { get { return typeof(T); } }
    }

    [ContentPropertyAttribute(Name = "Actions")]
    public abstract class TriggerBase : DependencyObject, IAttachedObject
    {
        public readonly static DependencyProperty ActionsProperty = DependencyProperty.Register(
            "Actions",
            typeof(TriggerActionCollection),
            typeof(TriggerBase),
            new PropertyMetadata(null));

        public TriggerActionCollection Actions
        {
            get
            {
                var value = (TriggerActionCollection)this.GetValue(ActionsProperty);

                if (value == null)
                {
                    value = new TriggerActionCollection();
                    if (AssociatedObject != null)
                    {
                        value.Attach(AssociatedObject);
                    }
                    this.SetValue(ActionsProperty, value);
                }

                return value;
            }
            set
            {
                if (AssociatedObject != null)
                {
                    var oldValue = (TriggerActionCollection)this.GetValue(ActionsProperty);
                    oldValue.Detach();

                    if (value != null)
                    {
                        value.Attach(AssociatedObject);
                    }
                }
                this.SetValue(ActionsProperty, value);
            }
        }

        protected DependencyObject AssociatedObject { get; private set; }

        protected virtual Type AssociatedObjectTypeConstraint { get { return typeof(DependencyObject); } }

        protected void InvokeActions(Object parameter)
        {
            var previewInvoke = PreviewInvoke;
            var cancel = false;

            if (previewInvoke != null)
            {
                var previewInvokeEventArgs = new PreviewInvokeEventArgs();

                previewInvoke(this, previewInvokeEventArgs);

                cancel = previewInvokeEventArgs.Cancelling;
            }

            if (cancel == false)
            {
                foreach (var a in Actions)
                {
                    a.InvokeAction(parameter);
                }
            }
        }

        public void Attach(DependencyObject dependencyObject)
        {
            if (dependencyObject == null)
            {
                throw new ArgumentNullException("dependencyObject");
            }

            if (!AssociatedObjectTypeConstraint.GetTypeInfo().IsAssignableFrom(dependencyObject.GetType().GetTypeInfo()))
            {
                throw new ArgumentException("Cannot attach to object, does not meet AssociatedObjectTypeConstraint", "dependencyObject");
            }

            AssociatedObject = dependencyObject;
            Actions.Attach(dependencyObject);
            OnAttached();
        }

        public void Detach()
        {
            OnDetaching();
            Actions.Detach();
            AssociatedObject = null;
        }

        protected virtual void OnAttached() { }

        protected virtual void OnDetaching() { }

        public event EventHandler<PreviewInvokeEventArgs> PreviewInvoke;

        DependencyObject IAttachedObject.AssociatedObject { get { return this.AssociatedObject; } }
    }
}
